# -*- coding: utf-8 -*-
from OpenGL.arrays import vbo
from OpenGL.GLUT import *
from OpenGL.GLU import *
from OpenGL.GL import *
from point import Point
from numpy import array
import sys, math, os

from obj_reader import Obj_Reader
from collections import namedtuple

EXIT = -1
FIRST = 0
SIZE = 2 #Verändert die Groesse des Objekts (1 = Standard)

points = []
middle_point = ()
x_angle = 0
y_angle = 0
z_angle = 0
vb = None

mouse_clicked = False

Boundingbox = namedtuple('Boundingbox', 'min_x min_y min_z max_x max_y max_z')
BBOX_ELEMENTS = 6
# neue Klasse Boundingbox um namedtuple erstellenzu koennen

def init(width, height):
   """ Initialize an OpenGL window """
   glClearColor(0.0, 0.0, 0.0, 0.0)         #background color
   glMatrixMode(GL_PROJECTION)              #switch to projection matrix
   glLoadIdentity()                         #set to 1
   gluPerspective(45., float(width)/height, 0.1, 100.0)
   glOrtho(-1.5, 1.5, -1.5, 1.5, -1.0, 1.0) #multiply with new p-matrix
   glMatrixMode(GL_MODELVIEW)               #switch to modelview matrix
   
   
def get_bounding_box(obj):
    """Gibt eine 3D BoundingBox anhand kleinseter und groesster Koordinaten, als tuple zurueck"""
    lx = [triple[0] for triple in obj.vertices]
    ly = [triple[1] for triple in obj.vertices]
    lz = [triple[2] for triple in obj.vertices]
    return Boundingbox(min(lx), min(ly), min(lz), max(lx), max(ly), max(lz))
    # Boundingbox ist ein collections.namedtuple

    
def get_middle_point(bbox):
   """gibt Mittelpunkt der 3D Boundingbox zurueck"""
   lis = map(float, bbox)
   lis = [(tup[0]+tup[1])/2 for tup in zip(lis[:3], lis[3:])]
   # packe mit zip: [(xmin,xmax), (ymin,ymax), (zmin,zmax)]

   return Point(lis[0], lis[1], lis[2])

def get_scalar_factor(bbox):
   """Gibt den Skalarfaktor anhand der Boundingbox zurück"""
   global SIZE
   scalar_factor = SIZE/(max(bbox.max_x-bbox.min_x, bbox.max_y-bbox.min_y,bbox.max_z-bbox.min_z))
   return scalar_factor
   
   
def display():
   global middle_point, scalar_factor, x_angle, y_angle, z_angle
   """ Render all objects"""
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) #clear screen
   # Set color to light gray
   glColor3f(.75, .75, .75)
   # Set drawstyle, steht nur FRONT hier wird objekt nur gezeichnet wenns im Uhrzeigersinn da steht
   glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
   # Render Vertex Buffer Object
   vb.bind()

   # Faehigkeit hinzufuegen
   glEnableClientState(GL_VERTEX_ARRAY)
   glEnableClientState(GL_NORMAL_ARRAY)
   # Zeichenmethode, start, anzahl
   glDrawArrays(GL_POINTS, 0, len(vb))
   vb.unbind()
   glDisableClientState(GL_VERTEX_ARRAY)
   glDisableClientState(GL_NORMAL_ARRAY)
   # Vorherige Transformationen rückgängig machen
   glLoadIdentity()
   
   # Rotation
   glRotatef(x_angle,1,0,0)
   glRotatef(y_angle,0,1,0)
   glRotatef(z_angle,0,0,1)

   # Skalierung
   glScalef(scalar_factor, scalar_factor, scalar_factor)

   # Translation
   glTranslatef(-middle_point.x, -middle_point.y, -middle_point.z)
   
   # Flush commands
   glutSwapBuffers()

def reshape(width, height):
   """ adjust projection matrix to window size"""
   glViewport(0, 0, width, height)
   glMatrixMode(GL_PROJECTION)
   glLoadIdentity()
   if width <= height:
       glOrtho(-1.5, 1.5,
               -1.5*height/width, 1.5*height/width,
               -1.0, 1.0)
   else:
       glOrtho(-1.5*width/height, 1.5*width/height,
               -1.5, 1.5,
               -1.0, 1.0)
   glMatrixMode(GL_MODELVIEW)


def keyPressed(key, x, y):
   """ handle keypress events """
   global x_angle, y_angle, z_angle
   
   if key == chr(27): # chr(27) = ESCAPE
       sys.exit()
   if key == 'x':
      x_angle +=10 % 360
   if key == 'X':
      x_angle -=10 % 360
   if key == 'y':
      y_angle +=10 % 360
   if key == 'Y':
      y_angle -=10 % 360      
   if key == 'z':
      z_angle +=10 % 360
   if key == 'Z':
      z_angle -=10 % 360      

   glutPostRedisplay()


def mouse(button, state, x, y):
   global mouse_clicked
   """ handle mouse events """
   if button == GLUT_LEFT_BUTTON and state == GLUT_DOWN:
       mouse_clicked = True


def mouseMotion(x,y):
   """ handle mouse motion """
   print "mouse motion at ", x, y


def menu_func(value):
   """ handle menue selection """
   print "menue entry ", value, "choosen..."
   if value == EXIT:
       sys.exit()
   glutPostRedisplay()


   
def main():
##   if len(sys.argv) != 2:
##      print "Falsche Parameterlaenge: %d" % (len(sys.argv))
##      sys.exit(EXIT)
##   f_name = sys.argv[1]
   global middle_point, scalar_factor, vb
   
   obj_reader = Obj_Reader("bunny.obj")
   obj_reader.read_file()

   # TODO:
   # Schwanecke: Verscheachtelte Listen sollte VBO verstehen![ [[a,b,c], [a,b,c],  [a,b,c]], ...  ]
   # Wenn keine Texturen gesetzt sind kann man davon ausgehen dass sie nirgends gesetzt sind
   faces = obj_reader.faces
   #faces = [f[i][j][k] for i in range(len(f)) for j in range(len(f[i])) for k in range(len(f[i][j]))]

   print faces[0]

   vb = vbo.VBO(array(faces, 'f'))
   
   bbox = get_bounding_box(obj_reader)
   middle_point = get_middle_point(bbox)
   scalar_factor = get_scalar_factor(bbox)

   
   # Hack for Mac OS X
   cwd = os.getcwd()
   glutInit(sys.argv)
   os.chdir(cwd)


   glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB)

   glutInitWindowSize(500, 500)
   glutCreateWindow("simple openGL/GLUT template")

   glutDisplayFunc(display)     #register display function

   
   glutReshapeFunc(reshape)     #register reshape function
   glutKeyboardFunc(keyPressed) #register keyboard function 
   glutMouseFunc(mouse)         #register mouse function
   glutMotionFunc(mouseMotion)  #register motion function
   #glutCreateMenu(menu_func)    #register menue function




   glutAddMenuEntry("First Entry",FIRST) #Add a menu entry
   glutAddMenuEntry("EXIT",EXIT)         #Add another menu entry
   glutAttachMenu(GLUT_RIGHT_BUTTON)     #Attach mouse button to menue

   init(500,500) #initialize OpenGL state

   glutMainLoop() #start even processing


if __name__ == "__main__":
   main()
